package com.ifonly.mvc;

import com.google.common.base.Strings;
import com.ifonly.activerecord.ActiveRecordFactory;
import com.ifonly.activerecord.Model;
import com.ifonly.activerecord.annotation.ARDao;
import com.ifonly.activerecord.exceptions.ActiveRecordException;
import com.ifonly.common.KeyValue;
import com.ifonly.config.Configuration;
import com.ifonly.handler.Handler;
import com.ifonly.logger.Logger;
import com.ifonly.render.Render;
import com.ifonly.render.RenderFactory;
import com.ifonly.upload.CosMultipartServletRequest;
import com.ifonly.utils.ClassUtils;
import com.ifonly.utils.RequestUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.List;

/**
 * @author <a href="mailto:ifonlymaster@163.com">ifonly</a>
 * @version 1.0 2015-12-10 13:17
 * @since JDK 1.6
 */
public class ActionHandler extends Handler {
    private ActionMapper actionMapper = ActionMapper.getInstance();
    private Logger logger;

    public ActionHandler() {
        logger = Logger.getLogger(ActionHandler.class);
    }

    public KeyValue<Action, String[]> getActionAndPathParams(String target) {
        if(!target.startsWith("/")){
            target = "/" + target;
        }

        Action action = actionMapper.getAction(target);
        String pathParamPart = null;

        while (action == null && target.contains("/")) {
            String tempPath;
            String tempPathParam;
            if (target.lastIndexOf("/") == 0) {
                tempPath = "/";
            } else {
                tempPath = target.substring(0, target.lastIndexOf("/"));
            }
            tempPathParam = target.substring(target.lastIndexOf("/")+1);

            action = actionMapper.getAction(tempPath);
            target = tempPath;
            if (pathParamPart == null) {
                pathParamPart = tempPathParam;
            } else {
                pathParamPart = tempPathParam + "/" + pathParamPart;
            }

            if (target.equals("/")) {
                break;
            }
        }

        String[] pathParams = new String[0];
        if (!Strings.isNullOrEmpty(pathParamPart)) {
            pathParams = pathParamPart.split("/");
        }
        return new KeyValue<Action, String[]>(action, pathParams);
    }

    @SuppressWarnings("unchecked")
    private Controller newController(Class<? extends Controller> controllerClass) throws IllegalAccessException, InstantiationException {
        Controller controller = controllerClass.newInstance();
        List<Field> fields = ClassUtils.findAnnotationFields(ARDao.class, controllerClass);
        for (Field field : fields) {
            Class<? extends Model> clazz = (Class<? extends Model>) field.getType();
            Model model = ActiveRecordFactory.getActiveRecord(clazz);
            if (model == null) {
                throw new ActiveRecordException("Model for class("+ clazz.getName() +") not found");
            }
            field.setAccessible(true);
            field.set(controller, model);
        }
        return controller;
    }

    @Override
    public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) {
        if (target.contains(".")) {
            return;
        }

        KeyValue<Action, String[]> keyValue = getActionAndPathParams(target);
        Action action = keyValue.getKey();
        String[] pathParams = keyValue.getValue();

        if (action == null) {
            RenderFactory.getErrorRender(404).init(request, response).render();
            isHandled[0] = true;
        } else {
            try {

                Class<? extends Controller> controllerClass = action.getControllerClass();
                Controller controller = newController(controllerClass);

                if (RequestUtils.isMultipart(request)) {
                    request = new CosMultipartServletRequest(request);
                }

                if (Configuration.getInstance().isDevMode()) {
                    ActionReporter.report(request, action, pathParams);
                }

                controller.init(request, response, pathParams);

                new ActionInvocation(action, controller).invoke();

                Render render = controller.getRender();
                if (render != null) {
                    render.init(request, response);
                    render.setViewDir(action.getViewDir());
                    render.render();
                }

                isHandled[0] = true;
            } catch (Exception e) {
                //有异常 显示到页面
                RenderFactory.getErrorRender(500).init(request, response).render();
                isHandled[0] = true;
                logger.error("action handler failed", e);
                e.printStackTrace();
                //throw new RuntimeException(e);
            }
        }
    }

}
